#include "canvas.hpp"
#include <SDL_gfxPrimitives.h>

namespace yep
{

Canvas::Canvas(SDL_Surface *surface_, bool freeAtEnd_)
{
	surface = surface_;
	owner = freeAtEnd_;
}

Canvas::~Canvas()
{
	if (owner)
	{
		SDL_FreeSurface(surface);
	}
}

Color Canvas::getPixel(const Vector2 & position) const
{
	int bpp = surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8*) ((surface->pixels))
			+ lround(position.y) * surface->pitch + lround(position.x) * bpp;
	Uint32 value = 0;
	switch (bpp)
	{
	case 1:
		value = *p;
		break;
	case 2:
		value = *(Uint16 *) p;
		break;

	case 3:
		if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
		{
			value = p[0] << 16 | p[1] << 8 | p[2];
		}
		else
		{
			value = p[0] | p[1] << 8 | p[2] << 16;
		}
		break;

	case 4:
		value = *(Uint32 *) p;
		break;

	default:
		value = 0; /* shouldn't happen, but avoids warnings */
		break;
	}
	return Color(value, surface->format);
}

void Canvas::setPixel(const Vector2 & position, const Color & color)
{
	int bpp = surface->format->BytesPerPixel;
	/* Here p is the address to the pixel we want to set */
	Uint8 *p = (Uint8*) ((surface->pixels))
			+ lround(position.y) * surface->pitch + lround(position.x) * bpp;
	Uint32 pixel = color(surface);
	switch (bpp)
	{
	case 1:
		*p = pixel;
		break;

	case 2:
		*(Uint16 *) p = pixel;
		break;

	case 3:
		if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
		{
			p[0] = (pixel >> 16) & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = pixel & 0xff;
		}
		else
		{
			p[0] = pixel & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = (pixel >> 16) & 0xff;
		}

		break;

	case 4:
		*(Uint32 *) p = pixel;
		break;
	}
}

void Canvas::putPixel(const Vector2 & position, const Color & color)
{
	pixelColor(surface, position.x, position.y, color);
}

void Canvas::clear(const Color & color)
{
	SDL_FillRect(surface, NULL, color(surface));
}

void Canvas::drawText(const Vector2 & position, const Color & color,
		const string & text)
{
	stringColor(surface, lround(position.x), lround(position.y), text.c_str(),
			color);
}

void Canvas::drawRectangle(const Vector2 & topLeft, const Vector2 & size,
		const Color & color)
{
	Vector2 bottomRight(topLeft);
	bottomRight += size + Vector2(-1, -1);
	rectangleColor(surface, lround(topLeft.x), lround(topLeft.y),
			lround(bottomRight.x), lround(bottomRight.y), color);
}

void Canvas::fillRectangle(const Vector2 & topLeft, const Vector2 & size,
		const Color & color)
{
	Vector2 bottomRight(topLeft);
	bottomRight += size + Vector2(-1, -1);
	boxColor(surface, lround(topLeft.x), lround(topLeft.y),
			lround(bottomRight.x), lround(bottomRight.y), color);
}

void Canvas::setClipArea(const Vector2 & topLeft, const Vector2 & size)
{
	SDL_Rect Area =
	{ (Sint16) (topLeft.x), (Sint16) (topLeft.y), (Uint16) (size.x),
			(Uint16) (size.y) };
	SDL_SetClipRect(surface, &Area);
}


void Canvas::setClipArea(const Vector2 & topLeft)
{
	setClipArea(topLeft, getSize());
}

void Canvas::clearClipArea()
{
	SDL_SetClipRect(surface, NULL);
}

}
